package org.icnmasjid.member.service.impl;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.icnmasjid.member.dao.Dao;
import org.icnmasjid.member.dao.IcnDataDao;
import org.icnmasjid.member.dao.PersonDao;
import org.icnmasjid.member.model.Address;
import org.icnmasjid.member.model.AfaReg;
import org.icnmasjid.member.model.IcnData;
import org.icnmasjid.member.model.IcnPayment;
import org.icnmasjid.member.model.MemberInformation;
import org.icnmasjid.member.model.MoodleGrade;
import org.icnmasjid.member.model.PaymentCategory;
import org.icnmasjid.member.model.Person;
import org.icnmasjid.member.model.PersonPayment;
import org.icnmasjid.member.model.PersonRelation;
import org.icnmasjid.member.model.PersonSubOrgRole;
import org.icnmasjid.member.model.RelationshipType;
import org.icnmasjid.member.model.SchoolPayment;
import org.icnmasjid.member.model.SchoolRegistration;
import org.icnmasjid.member.model.SubOrg;
import org.icnmasjid.member.service.ImportIcnDataManager;
import org.icnmasjid.member.service.LookupManager;
import org.icnmasjid.member.service.RegistrationManager;
import org.icnmasjid.member.service.RoleManager;
import org.icnmasjid.member.util.DateUtil;
import org.icnmasjid.member.util.StringUtil;
import org.joda.time.DateTime;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.Assert;

public class ImportIcnDataManagerImpl implements ImportIcnDataManager {
	private final Log log = LogFactory.getLog(ImportIcnDataManagerImpl.class);
	private IcnDataDao icnDataDao;
	private PersonDao personDao;
	private Dao dao;
	private RegistrationManager registrationManager;
	private RoleManager roleManager;
	private LookupManager lookupManager;

	public void importIcnPayments(boolean all) {
		// for each record in IcnPayment I need to figure out if I have to change the membership flag
		List<IcnPayment> list = getDao().getObjects(IcnPayment.class);
		// build a hashmap of lastname-firstname-first 6 chars of address
		HashMap<String, List<IcnPayment>>map = new HashMap<String, List<IcnPayment>>();
		for (IcnPayment i:list) {
			StringBuffer name = new StringBuffer();
			if ( StringUtils.isNotBlank(i.getLast())) {
				name.append(i.getLast().trim());
			}
			if ( StringUtils.isNotBlank(i.getFirst()) ) {
				name.append(i.getFirst().trim());
			}
			if ( StringUtils.isNotBlank(i.getAddress())) {
				name.append(
					i.getAddress().trim().substring(
							0,
							i.getAddress().trim().length() > 6
							? 6
							: i.getAddress().trim().length())
					);
			}
			List<IcnPayment> l = map.get(name.toString());
			if ( l == null ) {
				l = new ArrayList<IcnPayment>();
				map.put(name.toString(), l);
			}
			l.add(i);
		}

		for ( List<IcnPayment> li: map.values()) {
			// for each set search and if unique record found then update the membership if the dollar amount > 100
			double total = 0;
			for ( IcnPayment ip: li) {
				total += ip.getAmount();
			}
			if ( total >= 100) {
				searchAndUpdate(li, all);
			}
		}
	}

	public void searchAndUpdate(List<IcnPayment> li, boolean all) {
		List<Person> persons = null;
		Person search = new Person();
		IcnPayment icnPayment = li.get(0);
		search.setPersonStatus("Active");
		if ( StringUtils.isNotBlank(icnPayment.getLast()) ) {
			search.setLastName(icnPayment.getLast().trim());
		}
		if ( StringUtils.isNotBlank(icnPayment.getFirst()) ) {
			search.setFirstName(icnPayment.getFirst().trim());
		}
		if ( StringUtils.isNotBlank(icnPayment.getAddress()) && !icnPayment.getAddress().trim().equals(",,,") ) {
			Address add = new Address();
			add.setAddress1(
				icnPayment.getAddress().trim().length() > 6
				? icnPayment.getAddress().trim().substring(0,6)
			    : icnPayment.getAddress().trim()
			);
			search.setSearchAddress(add);
		}
		persons = getPersonDao().getPersons(search);
		if ( persons.size() == 0) {
			// try with interchanging the first and last name and same address
			if ( StringUtils.isNotBlank(icnPayment.getLast()) ) {
				search.setFirstName(icnPayment.getLast().trim());
			}
			if ( StringUtils.isNotBlank(icnPayment.getFirst()) ) {
				search.setLastName(icnPayment.getFirst().trim());
			}
			persons = getPersonDao().getPersons(search);
		}
		// try with email
		if ( persons.size() == 0 && StringUtils.isNotBlank(icnPayment.getEmail())) {
			search.setFirstName(null);
			search.setLastName(null);
			search.getSearchAddress().setAddress1(null);
			search.setEmail(icnPayment.getEmail());
			persons = getPersonDao().getPersons(search);
		}

		if (persons.size() == 0) {
			log.debug("No match found:" + icnPayment.toString());
		} else if ( persons.size() > 1) {
			log.debug("More than one match found:" + icnPayment.toString());
		} else {
			Person match = persons.get(0);
			Person primary = match.isPrimaryPerson() ? match : match.getPrimary();
			if ( !all || primary.isMember(2007) ) {
				log.debug("Updating Membership Status:" + primary.toString());
				match.setMember(true, 2008);
			} else {
				log.debug("Not a 2007 member:" + primary.toString());
			}
		}
	}

	public void importIcnData() {
		/*
		// for each record in icndata insert a person record
		List<IcnData> list = (List<IcnData>)icnDataDao.getNonDuplicateIcnData();
		for ( IcnData i : list) {
			doIt(i);
		}
		*/
		IcnData search = new IcnData();
		search.setSourceType("Election");
		search.setDuesPaid("Paid");
		List<IcnData> list = (List<IcnData>)icnDataDao.getIcnDatas(search);
		for ( IcnData i : list) {
			copyDuesPaid(i);
		}

	}

	private void copyDuesPaid(IcnData icnData) {
		Person p = new Person();
		if ( icnData.getSourceId() != null ) {
			// find a matching person
			p.setSourceId(icnData.getSourceId());
			p.setSourceType(icnData.getSourceType());
			p.setPrimaryPerson(Boolean.TRUE);
			List<Person> persons = personDao.getPersonsbyExample(p);
			if ( persons.size() == 1) {
				Person match = persons.iterator().next();
				MemberInformation mi = new MemberInformation(2007);
				mi.setPerson(match);
				match.getMemberInformations().add(mi);
				personDao.savePerson(match);
			}
		}
	}

	private void doIt(IcnData i) {

		Person p = new Person();
		if ( StringUtils.isNotBlank(i.getHusbandName())) {
			p.setFirstName(i.getHusbandName());
		} else if ( StringUtils.isBlank(i.getHusbandName()) && StringUtils.isBlank(i.getWifeName()) ) {
			p.setFirstName(i.getLastName());
		} else {
			p.setFirstName(i.getWifeName());
		}
		p.setLastName(i.getLastName());
		p.setEmail(i.getEmail());
		p.setEmail2(i.getEmail2());
		p.setPhoneHome(i.getHomePhone());
		p.setPhoneMobile(i.getCellPhone());
		p.setPhoneFax(i.getFaxPhone());
		p.setPhoneWork(i.getWorkPhone());
		p.setSourceId(i.getSourceId());
		p.setSourceType(i.getSourceType());
		//p.setGender("M");
		p.setCreatedDate(new Timestamp(System.currentTimeMillis()));
		p.setPrimaryPerson(Boolean.TRUE);

		Address a = new Address();
		a.setAddress1(i.getAddress1());
		a.setAddress2(i.getAddress2());
		a.setCity(i.getCity());
		a.setState(i.getState());
		a.setZip(i.getZip());
		a.setType("home");
		p.addAddress(a);

		personDao.savePerson(p);

		if ( StringUtils.isNotBlank(i.getHusbandName()) && StringUtils.isNotBlank(i.getWifeName()) ) {
			Person spouse = new Person();
			//spouse.setGender("F");
			spouse.setFirstName(i.getWifeName());
			spouse.setLastName(i.getLastName());
			spouse.setEmail(i.getSpouseEmail());
			spouse.setSourceId(i.getSourceId());
			spouse.setSourceType(i.getSourceType());
			spouse.setCreatedDate(new Timestamp(System.currentTimeMillis()));

			PersonRelation pr = new PersonRelation();
			pr.setPerson(p);
			pr.setRelatedPerson(spouse);
			pr.setRelationshipType(
				(RelationshipType)dao.getObject(RelationshipType.class, 1)
			);
			p.getPersonRelations().add(pr);
			personDao.savePerson(spouse);
		}
	}

	public IcnDataDao getIcnDataDao() {
		return icnDataDao;
	}

	public void setIcnDataDao(IcnDataDao icnDataDao) {
		this.icnDataDao = icnDataDao;
	}

	public Dao getDao() {
		return dao;
	}

	public void setDao(Dao dao) {
		this.dao = dao;
	}

	public PersonDao getPersonDao() {
		return personDao;
	}

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}
	
	public void importOnlineSchoolRegistration() {
		// I read all unprocessed school registrations and then try to import them
		SchoolRegistration filter = new SchoolRegistration();
		filter.setProcessed(false);
		List<SchoolRegistration> list = getRegistrationManager().getExistingSchoolRegistrations(filter);
		// lets sort by id order
		Collections.sort(
				list,
				new Comparator<SchoolRegistration>() {
					@Override
					public int compare(SchoolRegistration o1,
							SchoolRegistration o2) {
						return o1.getId().compareTo(o2.getId());
					}	
				}
		);
		for (SchoolRegistration sr : list) {
			try {
				log.debug(sr.toString());
				Person p = findMatchingPerson(sr);
				if ( p != null ) {
					if ( isParentMatch(p, sr) ) {
						updateInfoUsingParent(p, sr);
						if ( p.isPrimaryPerson() ) {
							updateOrCreateSpouse(p, sr, false);
						} else {
							updateInfoUsingSpouse(p.getPrimary(), sr);
						}
					} else {
						updateInfoUsingSpouse(p, sr);
						if ( p.isPrimaryPerson() ) {
							updateOrCreateSpouse(p, sr, true);
						} else {
							updateInfoUsingParent(p.getPrimary(), sr);
						}					
					}
					addOrUpdateChildren(p.isPrimaryPerson() ? p : p.getPrimary(), sr);
					personDao.savePerson(p);
				} else {
					createNewRecord(sr);
				}
				sr.setProcessed(true);
				dao.saveObject(sr);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void addOrUpdateChildren(Person p, SchoolRegistration sr) throws ParseException {
		// for each child see if this child already exists
		// if it already exists then update dob
		// if already student of 2011 then just move the grade up by 1
		// else put the grade specified in sr
		if ( StringUtils.isNotBlank(sr.getKid1Fname())) {
			addOrUpdateChild(
					p, 
					sr.getKid1Fname(), 
					sr.getKid1Lname(), 
					DateUtils.parseDate(sr.getKid1Dob(), new String[]{"MMMM dd yyyy"}), 
					sr.getKid1Grade(), 
					sr.getKid1Sex()
			);
		}
		if ( StringUtils.isNotBlank(sr.getKid2Fname())) {
			addOrUpdateChild(
					p, 
					sr.getKid2Fname(), 
					sr.getKid2Lname(), 
					DateUtils.parseDate(sr.getKid2Dob(), new String[]{"MMMM dd yyyy"}),
					sr.getKid2Grade(), 
					sr.getKid2Sex()
			);
		}
		if ( StringUtils.isNotBlank(sr.getKid3Fname())) {
			addOrUpdateChild(
					p, 
					sr.getKid3Fname(), 
					sr.getKid3Lname(), 
					DateUtils.parseDate(sr.getKid3Dob(), new String[]{"MMMM dd yyyy"}),
					sr.getKid3Grade(), 
					sr.getKid3Sex()
			);
		}
		if ( StringUtils.isNotBlank(sr.getKid4Fname())) {
			addOrUpdateChild(
					p, 
					sr.getKid4Fname(), 
					sr.getKid4Lname(), 
					DateUtils.parseDate(sr.getKid4Dob(), new String[]{"MMMM dd yyyy"}),
					sr.getKid4Grade(), 
					sr.getKid4Sex()
			);
		}
		if ( StringUtils.isNotBlank(sr.getKid5Fname())) {
			addOrUpdateChild(
					p, 
					sr.getKid5Fname(), 
					sr.getKid5Lname(), 
					DateUtils.parseDate(sr.getKid5Dob(), new String[]{"MMMM dd yyyy"}),
					sr.getKid5Grade(), 
					sr.getKid5Sex()
			);
		}
	}
	
	private void addOrUpdateChildren(Person p, AfaReg sr) throws ParseException {
		// for each child see if this child already exists
		// if it already exists then update dob
		// if already student of 2011 then just move the grade up by 1
		// else put the grade specified in sr
		if ( StringUtils.isNotBlank(sr.getStudentFullNameFirstName())) {
			addOrUpdateChild(
					p, 
					sr.getStudentFullNameFirstName(), 
					sr.getStudentFullNameLastName(), 
					DateUtils.parseDate(sr.getBirthDate(), new String[]{"MMMM dd yyyy"}), 
					sr.getGrade(), 
					sr.getGender()
			);
		}
		if ( StringUtils.isNotBlank(sr.getStudentFullNameFirstName2())) {
			addOrUpdateChild(
					p, 
					sr.getStudentFullNameFirstName2(), 
					sr.getStudentFullNameLastName2(), 
					DateUtils.parseDate(sr.getBirthDate2(), new String[]{"MMMM dd yyyy"}), 
					sr.getGrade2(), 
					sr.getGender2()
			);
		}
		if ( StringUtils.isNotBlank(sr.getStudentFullNameFirstName3())) {
			addOrUpdateChild(
					p, 
					sr.getStudentFullNameFirstName3(), 
					sr.getStudentFullNameLastName3(), 
					DateUtils.parseDate(sr.getBirthDate3(), new String[]{"MMMM dd yyyy"}), 
					sr.getGrade3(), 
					sr.getGender3()
			);
		}

		if ( StringUtils.isNotBlank(sr.getStudentFullNameFirstName4())) {
			addOrUpdateChild(
					p, 
					sr.getStudentFullNameFirstName4(), 
					sr.getStudentFullNameLastName4(), 
					DateUtils.parseDate(sr.getBirthDate4(), new String[]{"MMMM dd yyyy"}), 
					sr.getGrade4(), 
					sr.getGender4()
			);
		}

		if ( StringUtils.isNotBlank(sr.getStudentFullNameFirstName5())) {
			addOrUpdateChild(
					p, 
					sr.getStudentFullNameFirstName5(), 
					sr.getStudentFullNameLastName5(), 
					DateUtils.parseDate(sr.getBirthDate5(), new String[]{"MMMM dd yyyy"}), 
					sr.getGrade5(), 
					sr.getGender5()
			);
		}
	}

	
	private void addOrUpdateChild(Person p, String firstName, String lastName, Date dob, String grade, String gender) {
		Person child = null;
		for (PersonRelation pr: p.getPersonRelations()) {
			Person rp = pr.getRelatedPerson();
			if ( pr.getRelationshipType().isChild() && rp.getFirstName().trim().equalsIgnoreCase(firstName.trim())) {
				child = rp;
				break;
			}
		}
		if ( child == null ) {
			child = new Person();
			child.setCreatedDate(new Timestamp(System.currentTimeMillis()));		
			child.setPrimaryPerson(false);
			PersonRelation pr = new PersonRelation();
			pr.setPerson(p);
			pr.setRelatedPerson(child);
			pr.setRelationshipType(
				(RelationshipType)dao.getObject(RelationshipType.class, 2) // child
			);
			p.getPersonRelations().add(pr);
		}
		child.setFirstName(firstName);		
		child.setLastName(lastName);
		child.setDateOfBirth(new java.sql.Date(dob.getTime()));
		child.setGender(gender.substring(0, 1));
		child.setPersonStatus("Active");
		personDao.savePerson(child);
		addGrade(child, grade);
	}
	
	private void addGrade(Person student, String grade) {
		// if child is already a student of 2011 then just move the grade up by 1
		// else use grade supplied in sr
		if ( student.isWSStudent("2014") ) {
			return;
		}
		PersonSubOrgRole psor = new PersonSubOrgRole();
		psor.setPerson(student);
		psor.setRole(getRoleManager().getRole("Student-2014"));		
		if ( student.isWSStudent("2013")) {
			psor.setSubOrg(getNextSubOrg(student.getSubOrg("Student-2013"), student));			
		} else {
			// use load balancing for new students
			psor.setSubOrg(
					getSubOrg(
							grade, 
							"M".equalsIgnoreCase(
									student.getGender()
							), 
							student.getDateOfBirth()
					)
			);
		}
		Assert.notNull(psor.getSubOrg(), psor.toString());
		student.getPersonSubOrgRoles().add(psor);
	}

	private SubOrg getSubOrg(String grade, boolean isMale, Date dob) {
		/*
			1st
			2nd
			3rd
			4th
			5th
			6th
			KG
			preK
			Youth Level 1
			Youth Level 2
			Youth Level 3
			
			1st Grade-Section 1
			1st Grade-Section 2
			1st Grade-Section 3
			2nd Grade-Section 1
			2nd Grade-Section 2
			2nd Grade-Section 3
			3rd Grade-Section 1
			3rd Grade-Section 2
			4th Grade-Section 1
			4th Grade-Section 2
			5th Grade Section 1
			6th Grade Section 1
			Advanced Track
			KG Section 1
			KG Section 2
			Level1-Boys
			Level1-Girls
			Level2-Boys
			Level2-Girls
			Level3-Boys
			Level3-Girls
			PreK-Section 1
			PreK-Section 2
			Special Needs
			Waiting List
			WeekendSchool-General
		 */
		List<SubOrg> suborgs = dao.getObjects(SubOrg.class);
		Map<String, SubOrg> suborgMap = new HashMap<String, SubOrg>();
		for (SubOrg so: suborgs) {
			suborgMap.put(so.getName(), so);
		}
		Map<String, List<String>> map = new HashMap<String, List<String>>();
		map.put("1st Grade", Arrays.asList(new String[]{"1st Grade-Section 1","1st Grade-Section 2"}));
		map.put("2nd Grade", Arrays.asList(new String[]{"2nd Grade-Section 1","2nd Grade-Section 2"}));
		map.put("3rd Grade", Arrays.asList(new String[]{"3rd Grade-Section 1","3rd Grade-Section 2"}));
		map.put("4th Grade", Arrays.asList(new String[]{"4th Grade-Section 1","4th Grade-Section 2"}));
		map.put("5th Grade", Arrays.asList(new String[]{"5th Grade-Section 1","5th Grade-Section 2"}));
		map.put("6th Grade", Arrays.asList(new String[]{"6th Grade-Section 1","6th Grade-Section 2"}));
		map.put("7th Grade", Arrays.asList(new String[]{"7th Grade-Section 1"}));
		map.put("KG", Arrays.asList(new String[]{"KG Section 1","KG Section 2"}));
		map.put("Pre-KG", Arrays.asList(new String[]{"PreK-Section 1","PreK-Section 2"}));
		
		if ( map.containsKey(grade) ) {
			SubOrg so = null;
			Long count = -1L;
			for (String grade1 : map.get(grade) ) {
				Long temp = personDao.getGradeCount(grade1, "Student-2014");
				log.debug("---------------grade: " + grade);
				log.debug("---------------grade1: " + grade1);
				log.debug("---------------temp: " + temp);
				if ( count == -1L || temp <= count ) {
					count = temp;
					so = suborgMap.get(grade1);
				}
			}
			log.debug("---------------so: " + so.getName());
			return so;
		}
		if ( "Youth Program".equalsIgnoreCase(grade) ) {
			// if you are less than 14 then you go to ypj
			/*
			try {
				
				if ( dob.after(DateUtil.convertStringToDate("10/01/1999")) ) {
					return (SubOrg)dao.getObject(SubOrg.class, 35); // move to ypj
				} else {
				*/
					if ( isMale ) {
						return (SubOrg)dao.getObject(SubOrg.class, 32); // move to ypb
					} else {
						return (SubOrg)dao.getObject(SubOrg.class, 33); // move to pyg
					}
					/*
				}
			} catch (ParseException e) {
				return null;
			}
			*/
		}
		return null;
	}

	private void updateOrCreateSpouse(
			Person p,
			SchoolRegistration sr,
			boolean useParentInfo
	) {		
		if ( StringUtils.isNotBlank(sr.getSfname()) ) {
			Person spouse = null;
			if ( p.getSpouse() == null ) {
				spouse = new Person();
				spouse.setCreatedDate(new Timestamp(System.currentTimeMillis()));				
				PersonRelation pr = new PersonRelation();
				pr.setPerson(p);
				pr.setRelatedPerson(spouse);
				pr.setRelationshipType(
					(RelationshipType)dao.getObject(RelationshipType.class, 1)
				);
				p.getPersonRelations().add(pr);
	
			} else {
				spouse = p.getSpouse();
			}
			if ( useParentInfo ) {
				updateInfoUsingParent(spouse, sr);
			} else {
				updateInfoUsingSpouse(spouse, sr);
			}
		}
	}
	
	private void updateOrCreateSpouse(
			Person p,
			AfaReg sr,
			boolean useParentInfo
	) {		
		if ( StringUtils.isNotBlank(sr.getSpouseNameFirstName()) ) {
			Person spouse = null;
			if ( p.getSpouse() == null ) {
				spouse = new Person();
				spouse.setCreatedDate(new Timestamp(System.currentTimeMillis()));				
				PersonRelation pr = new PersonRelation();
				pr.setPerson(p);
				pr.setRelatedPerson(spouse);
				pr.setRelationshipType(
					(RelationshipType)dao.getObject(RelationshipType.class, 1)
				);
				p.getPersonRelations().add(pr);
	
			} else {
				spouse = p.getSpouse();
			}
			if ( useParentInfo ) {
				updateInfoUsingParent(spouse, sr);
			} else {
				updateInfoUsingSpouse(spouse, sr);
			}
		}
	}


	private boolean isParentMatch(Person p, SchoolRegistration sr) {
		// first use parent sr info to match
		boolean result =
				sr.getPfname().equalsIgnoreCase(p.getFirstName())
			||	sr.getPemail().equalsIgnoreCase(p.getEmail())
			||	sr.getPemail().equalsIgnoreCase(p.getEmail2())
			||  StringUtil.stripNonDigits(sr.getPmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneHome()))
			||  StringUtil.stripNonDigits(sr.getPmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneMobile()))
			||  StringUtil.stripNonDigits(sr.getPmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneWork()))
			||  StringUtil.stripNonDigits(sr.getPmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneFax()));
		if ( result ) {
			return true;
		}
		// then use spouse sr info to match
		result =
			sr.getSfname().equalsIgnoreCase(p.getFirstName())
			||	sr.getSemail().equalsIgnoreCase(p.getEmail())
			||	sr.getSemail().equalsIgnoreCase(p.getEmail2())
			||  StringUtil.stripNonDigits(sr.getSmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneHome()))
			||  StringUtil.stripNonDigits(sr.getSmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneMobile()))
			||  StringUtil.stripNonDigits(sr.getSmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneWork()))
			||  StringUtil.stripNonDigits(sr.getSmphone()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneFax()));
		
		if ( result ) {
			return false;
		}
		return true;
	}
	
	private boolean isParentMatch(Person p, AfaReg sr) {
		// first use parent sr info to match
		boolean result =
				sr.getParentNameFirstName().trim().equalsIgnoreCase(p.getFirstName().trim())
			||	sr.getEmail().trim().equalsIgnoreCase(p.getEmail())
			||	sr.getEmail().trim().equalsIgnoreCase(p.getEmail2())
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneHome()))
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneMobile()))
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneWork()))
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneFax()));
		if ( result ) {
			return true;
		}
		// then use spouse sr info to match
		result =
			sr.getSpouseNameFirstName().trim().equalsIgnoreCase(p.getFirstName().trim())
			||	sr.getEmail2().trim().equalsIgnoreCase(p.getEmail())
			||	sr.getEmail2().trim().equalsIgnoreCase(p.getEmail2())
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber2()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneHome()))
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber2()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneMobile()))
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber2()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneWork()))
			||  StringUtil.stripNonDigits(sr.getMobilePhoneNumber2()).equalsIgnoreCase(StringUtil.stripNonDigits(p.getPhoneFax()));
		
		if ( result ) {
			return false;
		}
		return true;
	}


	private void createNewRecord(SchoolRegistration sr) throws ParseException {
		Person primary = new Person();
		primary.setCreatedDate(new Timestamp(System.currentTimeMillis()));		
		primary.setPrimaryPerson(Boolean.TRUE);
		updateInfoUsingParent(primary, sr);
		updateOrCreateSpouse(primary, sr, false);
		addOrUpdateChildren(primary, sr);
		getPersonDao().savePerson(primary);
	}
	
	private Person createNewRecord(AfaReg sr) throws ParseException {
		Person primary = new Person();
		primary.setCreatedDate(new Timestamp(System.currentTimeMillis()));		
		primary.setPrimaryPerson(Boolean.TRUE);
		updateInfoUsingParent(primary, sr);
		updateOrCreateSpouse(primary, sr, false);
		addOrUpdateChildren(primary, sr);
		getPersonDao().savePerson(primary);
		return primary;
	}


	private void updateInfoUsingParent(Person p, SchoolRegistration sr) {
		if ( StringUtils.isNotBlank(sr.getPfname()) ) {
			p.setFirstName(sr.getPfname().trim());
		}
		if ( StringUtils.isNotBlank(sr.getPlname()) ) {
			p.setLastName(sr.getPlname().trim());
		}				
		if ( StringUtils.isNotBlank(sr.getPemail()) ) {
			p.setEmail(sr.getPemail().trim());
		}
		if ( StringUtils.isNotBlank(sr.getPhomephone()) ) {
			p.setPhoneHome(StringUtil.stripNonDigits(sr.getPhomephone()));
		}
		if ( StringUtils.isNotBlank(sr.getPmphone()) ) {
			p.setPhoneMobile(StringUtil.stripNonDigits(sr.getPmphone()));
		}	
		Address a = null;
		if ( p.getAddresses().size() == 0 ) {
			a = new Address();
			p.getAddresses().add(a);
		} else {
			a = p.getAddresses().iterator().next();			
		}
		a.setAddress1(sr.getAddr1());
		a.setAddress2(sr.getAddr2());
		a.setCity(sr.getCity());
		a.setZip(sr.getZip());
		a.setType("Home");
		p.setPersonStatus("Active");
		personDao.savePerson(p);
	}
	
	private void updateInfoUsingParent(Person p, AfaReg sr) {
		if ( StringUtils.isNotBlank(sr.getParentNameFirstName()) ) {
			p.setFirstName(sr.getParentNameFirstName().trim());
		}
		if ( StringUtils.isNotBlank(sr.getParentNameLastName()) ) {
			p.setLastName(sr.getParentNameLastName().trim());
		}				
		if ( StringUtils.isNotBlank(sr.getEmail()) ) {
			p.setEmail(sr.getEmail().trim());
		}
		if ( StringUtils.isNotBlank(sr.getHomePhoneNumber()) ) {
			p.setPhoneHome(StringUtil.stripNonDigits(sr.getHomePhoneNumber()));
		}
		if ( StringUtils.isNotBlank(sr.getMobilePhoneNumber()) ) {
			p.setPhoneMobile(StringUtil.stripNonDigits(sr.getMobilePhoneNumber()));
		}	
		Address a = null;
		if ( p.getAddresses().size() == 0 ) {
			a = new Address();
			p.getAddresses().add(a);
		} else {
			a = p.getAddresses().iterator().next();			
		}
		a.setAddress1(sr.getAddressStreetAddress());
		a.setAddress2(sr.getAddressStreetAddressLine2());
		a.setCity(sr.getAddressCity());
		a.setState(sr.getAddressStateProvince());
		a.setZip(sr.getAddressPostalZipCode());
		a.setType("Home");
		p.setPersonStatus("Active");
		personDao.savePerson(p);
	}

	
	private void updateInfoUsingSpouse(Person p, SchoolRegistration sr) {
		if ( StringUtils.isNotBlank(sr.getSfname()) ) {
			p.setFirstName(sr.getSfname().trim());
		}
		if ( StringUtils.isNotBlank(sr.getSlname()) ) {
			p.setLastName(sr.getSlname().trim());
		}						
		if ( StringUtils.isNotBlank(sr.getSemail()) ) {
			p.setEmail(sr.getSemail().trim());
		}
		if ( StringUtils.isNotBlank(sr.getSmphone()) ) {
			p.setPhoneMobile(StringUtil.stripNonDigits(sr.getSmphone()));
		}		
		p.setPersonStatus("Active");
		personDao.savePerson(p);
	}
	
	private void updateInfoUsingSpouse(Person p, AfaReg sr) {
		if ( StringUtils.isNotBlank(sr.getSpouseNameFirstName()) ) {
			p.setFirstName(sr.getSpouseNameFirstName().trim());
		}
		if ( StringUtils.isNotBlank(sr.getSpouseNameLastName()) ) {
			p.setLastName(sr.getSpouseNameLastName().trim());
		}						
		if ( StringUtils.isNotBlank(sr.getEmail2()) ) {
			p.setEmail(sr.getEmail2().trim());
		}
		if ( StringUtils.isNotBlank(sr.getMobilePhoneNumber2()) ) {
			p.setPhoneMobile(StringUtil.stripNonDigits(sr.getMobilePhoneNumber2()));
		}		
		p.setPersonStatus("Active");
		personDao.savePerson(p);
	}
	
	
	private Person findMatchingPerson(SchoolRegistration sr) {
		List<String> emails = new ArrayList<String>();
		List<String> numbers = new ArrayList<String>();
		if ( StringUtils.isNotBlank(sr.getPemail())) {
			emails.add(sr.getPemail());
		}
		if ( StringUtils.isNotBlank(sr.getSemail())) {
			emails.add(sr.getSemail());
		}
		
		if ( StringUtils.isNotBlank(sr.getPhomephone())) {
			numbers.add(StringUtil.stripNonDigits(sr.getPhomephone()));
		}
		if ( StringUtils.isNotBlank(sr.getPmphone())) {
			numbers.add(StringUtil.stripNonDigits(sr.getPmphone()));
		}
		if ( StringUtils.isNotBlank(sr.getSmphone())) {
			numbers.add(StringUtil.stripNonDigits(sr.getSmphone()));
		}		
		
		if ( emails.size() == 0) {
			emails.add("na");
		}
		if ( numbers.size() == 0) {
			numbers.add("na");
		}
		
		List<Person> persons = getPersonDao().getPersons(emails, numbers);
		for ( Person p : persons) {
			if ( p.isPrimaryPerson() ) {
				return p;
			}
		}
		for ( Person p : persons) {
			if ( !p.isWSStudent() ) {
				return p;
			}
		}
		return persons.size() > 0 ? persons.get(0) : null;
	}
	
	private Person findMatchingPerson(AfaReg ar) {
		List<String> emails = new ArrayList<String>();
		List<String> numbers = new ArrayList<String>();
		if ( StringUtils.isNotBlank(ar.getEmail())) {
			emails.add(ar.getEmail());
		}
		if ( StringUtils.isNotBlank(ar.getEmail2())) {
			emails.add(ar.getEmail2());
		}
		
		if ( StringUtils.isNotBlank(ar.getHomePhoneNumber())) {
			numbers.add(StringUtil.stripNonDigits(ar.getHomePhoneNumber()));
		}
		if ( StringUtils.isNotBlank(ar.getMobilePhoneNumber())) {
			numbers.add(StringUtil.stripNonDigits(ar.getMobilePhoneNumber()));
		}
		if ( StringUtils.isNotBlank(ar.getMobilePhoneNumber2())) {
			numbers.add(StringUtil.stripNonDigits(ar.getMobilePhoneNumber2()));
		}		
		
		if ( emails.size() == 0) {
			emails.add("na");
		}
		if ( numbers.size() == 0) {
			numbers.add("na");
		}
		
		List<Person> persons = getPersonDao().getPersons(emails, numbers);
		for ( Person p : persons) {
			if ( p.isPrimaryPerson() ) {
				return p;
			}
		}
		for ( Person p : persons) {
			if ( !p.isWSStudent() ) {
				return p;
			}
		}
		return persons.size() > 0 ? persons.get(0) : null;
	}

	public RoleManager getRoleManager() {
		return roleManager;
	}

	public void setRoleManager(RoleManager roleManager) {
		this.roleManager = roleManager;
	}
    
	private SubOrg getNextSubOrg(SubOrg so, Person student) {    	
    	if ( so.getName().matches("PreK-.*") ) {
    		return findSubOrg(so.getName().replaceAll("PreK-", "KG "));
    	}
    	if ( so.getName().matches("KG .*") ) {
    		return findSubOrg(so.getName().replaceAll("KG ", "1st Grade-"));
    	}
    	if ( so.getName().matches("1st.*") ) {
    		return findSubOrg(so.getName().replaceAll("1st", "2nd"));
    	}
    	if ( so.getName().matches("2nd.*") ) {
    		return findSubOrg(so.getName().replaceAll("2nd", "3rd"));
    	}
    	if ( so.getName().matches("3rd.*") ) {
    		return findSubOrg(so.getName().replaceAll("3rd", "4th"));
    	}
    	if ( so.getName().matches("4th.*") ) {
    		return findSubOrg(so.getName().replaceAll("4th", "5th"));
    	}
    	if ( so.getName().matches("5th.*") ) {
    		return findSubOrg(so.getName().replaceAll("5th", "6th"));
    	}
    	if ( so.getName().matches("6th.*") ) {
    		return findSubOrg("7th Grade-Section 1");
    	}
    	if ( so.getName().matches("7th.*") ) {
    		if ( "M".equals(student.getGender()) ) {
    			return findSubOrg("Youth-Program-Boys");
    		} else {
    			return findSubOrg("Youth-Program-Girls");
    		}
    	}    	
    	if ( so.getName().matches("Youth-Program-Boys") ) {
    		return findSubOrg("Youth-Program-Boys");
    	}
    	if ( so.getName().matches("Youth-Program-Girls") ) {
    		return findSubOrg("Youth-Program-Girls");
    	}
    	if ( so.getName().matches("Youth-Program-Junior") ) {
    		if ( "M".equals(student.getGender()) ) {
    			return findSubOrg("Youth-Program-Boys");
    		} else {
    			return findSubOrg("Youth-Program-Girls");
    		}
    	}
    	if ( so.getName().matches("Special Needs") ) {
    		return so;
    	}    	
    	return null;
    }
    
    private SubOrg findSubOrg(String name) {
    	List<SubOrg> sos = getRoleManager().getSubOrgs();
    	for ( SubOrg so : sos ) {
    		if ( so.getName().equalsIgnoreCase(name) ) {
    			return so;
    		}
    	}
    	return null;
    }

	public RegistrationManager getRegistrationManager() {
		return registrationManager;
	}

	public void setRegistrationManager(RegistrationManager registrationManager) {
		this.registrationManager = registrationManager;
	}
	
	public void importSchoolFees() throws Exception{
		// I need to find the primary person using the email and update the payment for ws fees
		List<SchoolPayment> list = dao.getObjects(SchoolPayment.class);
		List<PaymentCategory> l = getLookupManager().getPaymentCategories();
		PaymentCategory pc = null;
		for (PaymentCategory o : l) {
			if ( "WeekendSchoolFees2014".equals(o.getName()) ) {
				pc = o;
			}
		}
		for (SchoolPayment sp : list) {
			if ( sp.getProcessed() ) {
				//log.error("Already Processed");
				//log.error(sp);
				continue;
			}
			Person p = personDao.getPersonByEmail(sp.getEmail().trim());
			if ( p == null ) {
				log.error("Unable to find person with email: " + sp.getEmail());
				log.error(sp);
				sp.setComments("Unable to find person with email: " + sp.getEmail());
				continue;
			} else {
				if ( !p.isPrimaryPerson() ) {
					p = p.getPrimary();
				}
				boolean duplicateExists = false;
				// if person has payment with same amount / date and payment category then don't process it and show as error
				for ( PersonPayment pp: p.getPersonPayments() ) {
					if ( 
							pp.getPaymentAmount() == sp.getAmount() 
						&&	pp.getPaymentDate().equals(DateUtil.convertStringToDate("MM/dd/yy",sp.getPaymentDate()))
						&&	pp.getPaymentCategory().getName().equals("WeekendSchoolFees2014")						
						&&  ( 		!pp.getPaymentMethod().contains("paypal - ") 
								||  
									(
												pp.getPaymentMethod().contains("paypal - ")
											&& 	pp.getPaymentMethod().contains(sp.getReferenceId())
									)
							)
						
					) {
						log.error("Duplicate payment found");
						sp.setComments("Duplicate payment found");
						log.error(sp);
						duplicateExists = true;
					}
				}
				if ( !duplicateExists ) {
					p.addPersonPayment(sp.getAmount(), pc, DateUtil.convertStringToDate("MM/dd/yy",sp.getPaymentDate()), "paypal - "+sp.getReferenceId());
					sp.setProcessed(true);
					personDao.savePerson(p);
				}
			}
		}
	}
	
	public void importMoodleGrade() {
		List<MoodleGrade> list = dao.getObjects(MoodleGrade.class);
		for (MoodleGrade mg : list) {
			Person p = personDao.getPerson(mg.getStudentId());
			if ( p == null ) {
				log.error("Id not found: " + mg.getStudentId());
				log.error(mg);
				continue;
			}
			String roleName = "Student-2013";
			SubOrg so = p.getSubOrg(roleName);
			PersonSubOrgRole psor = null;
			if ( so == null ) {
				psor = new PersonSubOrgRole();
				psor.setPerson(p);
				psor.setRole(lookupManager.getRoleByName(roleName));
				psor.setSubOrg(getNextSubOrg(p.getSubOrg("Student-2012"), p));
				p.getPersonSubOrgRoles().add(psor);
			} else {
				log.error("student already registered: " + mg.getStudentId());
				// ignore this student if he already has a record for the current year
				/*
				for ( PersonSubOrgRole t : p.getPersonSubOrgRoles() ) {
					if ( t.getRole().getName().equalsIgnoreCase(roleName)) {
						psor = t;
						break;
					}
				}
				*/
				//log.error("Match Found - Replacing Suborg from: " + psor.getSubOrg().getName());
				//log.error(mg);
				//psor.setSubOrg(lookupManager.getSubOrgByName(match(mg.getGrade())));
			}
			personDao.savePerson(p);
			mg.setProcessed(Boolean.TRUE);
			dao.saveObject(mg);
		}
	}
	
	private String match(String moodleGrade) {
		if ( moodleGrade.matches(".*Grade Section.*") ) {
    		return moodleGrade.replaceAll("Grade Section", "Grade-Section");
    	}
		return moodleGrade;
	}

	public LookupManager getLookupManager() {
		return lookupManager;
	}

	public void setLookupManager(LookupManager lookupManager) {
		this.lookupManager = lookupManager;
	}
	
	public void importAfaRegistration() {
		// I read all unprocessed afa registrations and then try to import them
		AfaReg filter = new AfaReg();
		filter.setProcessed(false);
		List<AfaReg> list = getRegistrationManager().getAfaRegistrations(filter);
		for (AfaReg ar : list) {
			log.debug(ar.toString());
			if (ar.getIcnAfaRegistration().trim().equals("NEW STUDENTS")) {
				// new student registration
				try {
					Person p = findMatchingPerson(ar);
					if ( p != null ) {
						if ( isParentMatch(p, ar) ) {
							updateInfoUsingParent(p, ar);
							if ( p.isPrimaryPerson() ) {
								updateOrCreateSpouse(p, ar, false);
							} else {
								updateInfoUsingSpouse(p.getPrimary(), ar);
							}
						} else {
							updateInfoUsingSpouse(p, ar);
							if ( p.isPrimaryPerson() ) {
								updateOrCreateSpouse(p, ar, true);
							} else {
								updateInfoUsingParent(p.getPrimary(), ar);
							}					
						}
						addOrUpdateChildren(p.isPrimaryPerson() ? p : p.getPrimary(), ar);
						personDao.savePerson(p);
					} else {
						p = createNewRecord(ar);
					}
					ar.setProcessed(true);
					dao.saveObject(ar);
				} catch (ParseException e) {
					e.printStackTrace();
					ar.setProcessed(false);
					ar.setComments(e.getMessage());
				}
			} else {
				// existing student registration
				// try to find the person by student id
				// match the matching student with the name and then update the grade
				try {
					handleExisingStudent(ar.getStudent1Id(), ar.getFullNameFirstName(), ar.getFullNameLastName(), ar.getParentsEmail());
					handleExisingStudent(ar.getStudent2Id(), ar.getFullNameFirstName2(), ar.getFullNameLastName2(), ar.getParentsEmail());
					handleExisingStudent(ar.getStudent3Id(), ar.getFullNameFirstName3(), ar.getFullNameLastName3(), ar.getParentsEmail());
					handleExisingStudent(ar.getStudent4Id(), ar.getFullNameFirstName4(), ar.getFullNameLastName4(), ar.getParentsEmail());
					handleExisingStudent(ar.getStudent5Id(), ar.getFullNameFirstName5(), ar.getFullNameLastName5(), ar.getParentsEmail());
					ar.setProcessed(true);
				} catch (IllegalArgumentException e) {
					ar.setProcessed(false);
					ar.setComments(e.getMessage());
				}
				
				dao.saveObject(ar);
			}
		}
	}
	
	private void handleExisingStudent(String studentId, String firstName, String lastName, String parentsEmail) {
		Person p = null;
		if ( StringUtils.isNotBlank(studentId) && StringUtils.isNumeric(studentId) ) {
			try {
				p = personDao.getPerson(Integer.valueOf(studentId));
				if ( 
						p.isWSStudent() 
					&& 
						p.getFirstName().trim().equalsIgnoreCase(firstName.trim()) 
				) {
					addGrade(p, null);
				} else {
					handleNonMatchingStudent(studentId, firstName, lastName, parentsEmail);
				}
			} catch (ObjectRetrievalFailureException e) {
				handleNonMatchingStudent(studentId, firstName, lastName, parentsEmail);
			}
		} else {
			if ( StringUtils.isNotBlank(studentId) ) {
				handleNonMatchingStudent(studentId, firstName, lastName, parentsEmail);
			}
		}

	}
	
	private void handleNonMatchingStudent(String studentId, String firstName, String lastName, String parentsEmail) {
		Person parent = personDao.getPersonByEmail(parentsEmail);
		if ( parent != null ) {
			// try to find the child by name
			Person primary = parent.isPrimaryPerson() ? parent : parent.getParent();
			for (Person s : primary.getStudents("Student-2013") ) {
				int length = firstName.trim().length() > 2 ? 3 : firstName.trim().length();
				if ( 
						s.getFirstName().trim().substring(0, length).equalsIgnoreCase(firstName.trim().substring(0, length))
				) {
					addGrade(s, "");
					return;
				}
			}
		}
		log.error("*****ID and name dont match: " + studentId);
		throw new IllegalArgumentException("ID and name dont match: " + studentId + " " + firstName + " " + lastName + " " + parentsEmail);
	}
	
}
